package com.sunseaypw.console;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

import javax.mail.internet.InternetAddress;
import javax.xml.catalog.Catalog;

import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
import com.sunseaypw.base.MyLogger;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.ReferenceCountUtil;

public class NioAioStudy {
    public static void main( String[] args )
    {        
        MyLogger.info("开始进来了","main");
        System.out.println( "Hello World!" );
        var c =new NioAioStudy();
        if(c!=null){
            c.bufferHandle();
            c.channelHandle();
            //c.servSocketChannelHandle();
            //c.socketChannelHandle();
            //c.aioFileHandle();
            //c.aioServerSocket();
            //c.aioServerSocket222();
            c.nettyServer();
        }
        MyLogger.warn("结束了","main2222");
    }
    public void nettyClient(){   
        //客户端一个线程组即可    
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //客户端的辅助类
            Bootstrap bootstrap  = new Bootstrap()
                    .group(group)
                    //客户端的通道类型
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() { // (4)
                        @Override
                        public void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                            pipeline.addLast("decoder", new StringDecoder(Charset.forName("utf-8")));
                            pipeline.addLast("encoder", new StringEncoder(Charset.forName("utf-8")));
                            pipeline.addLast("handler", new SimpleChannelInboundHandler<String>(){                                 
                                @Override
                                public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                    System.out.println(msg.toString());
                                }  
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)            
                                    if(msg instanceof String){
                                        System.out.println("接收到："+msg);                                                        
                                    }else{
                                        ByteBuf in = (ByteBuf) msg;   
                                        System.out.println("接收到222："+msg);  
                                    }          
                                    // try {
                                    //     while (in.isReadable()) { // (1)
                                    //         System.out.print((char) in.readByte());
                                    //         System.out.flush();
                                    //     }
                                    // } finally {
                                    //     //Please keep in mind that it is the handler's responsibility to release any reference-counted object passed to the handler.
                                    //     ReferenceCountUtil.release(msg); // (2)
                                    // }  
                                    //no release It is because Netty releases it for you when it is written out to the wire.            
                                        
                                }                              
                                @Override
                                public boolean acceptInboundMessage(Object msg) {
                                    System.out.println(msg);
                                    return true;
                                }
                            });
                        }
                    });
            Channel channel = bootstrap.connect("127.0.0.1", 9999).sync().channel();
            Scanner sc = new Scanner(System.in,"GBK");//必须设置vscode 脚本为 @chcp.com 936 > NUL  然后再次设置GBK编码，否则异常
            System.out.println("please enter...");
            boolean exit = false;            
            while(!exit){// 输入exit，退出系统dfdf
                String str = sc.nextLine();
                channel.writeAndFlush(Unpooled.copiedBuffer(("客户端："+str+"\r\n").getBytes("UTF-8")));                
                if(str.equalsIgnoreCase("exit")){
                    exit = true;
                    channel.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }    
    }    
    public void nettyServer(){
        /*NioEventLoopGroup is a multithreaded event loop that handles I/O operation. 
            Netty provides various EventLoopGroup implementations for different kind of transports. 
            We are implementing a server-side application in this example, and therefore two NioEventLoopGroup will be used. 
            The first one, often called 'boss', accepts an incoming connection. 
            The second one, often called 'worker', handles the traffic of the accepted connection once the boss accepts 
                the connection and registers the accepted connection to the worker. 
            How many Threads are used and how they are mapped to the created Channels 
                depends on the EventLoopGroup implementation and may be even configurable via a constructor.
        */
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            /*ServerBootstrap is a helper class that sets up a server. You can set up the server using a Channel directly. 
            However, please note that this is a tedious process, and you do not need to do that in most cases.
            */
            ServerBootstrap b = new ServerBootstrap(); // (2) 
            b.group(bossGroup, workerGroup)
              //we specify to use the NioServerSocketChannel class which is used to instantiate a new Channel to accept incoming connections.
             .channel(NioServerSocketChannel.class) // (3)
             /*The handler specified here will always be evaluated by a newly accepted Channel. 
                The ChannelInitializer is a special handler that is purposed to help a user configure a new Channel. 
                It is most likely that you want to configure the ChannelPipeline of the new Channel by adding some handlers 
                    such as DiscardServerHandler to implement your network application. 
                As the application gets complicated, it is likely that you will add more handlers to the pipeline 
                    and extract this anonymous class into a top-level class eventually.
             */
             .childHandler(new ChannelInitializer<io.netty.channel.socket.SocketChannel>() { // (4)
                 @Override
                 public void initChannel(io.netty.channel.socket.SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));                            
                    ch.pipeline().addLast("decoder", new StringDecoder(Charset.forName("utf-8")));
                    ch.pipeline().addLast("encoder", new StringEncoder(Charset.forName("utf-8")));
                    ch.pipeline().addLast(new NioAioStudy.DiscardServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
    
            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(9999).sync(); // (7)
    
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        }catch(Exception ex){
            ex.printStackTrace();
        }
        finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
    /**
     * Handles a server-side channel.
     */
    public static class DiscardServerHandler extends SimpleChannelInboundHandler<String> { // (1)
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception { // (4)
            try{
                Channel incoming = ctx.channel();
                incoming.writeAndFlush("server reply：123\n");
                incoming.writeAndFlush("[" + incoming.remoteAddress() + "]" + s + "\n");
            }catch(Exception ex){
                ex.printStackTrace();
            }            
        }
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2) 
            try{
                if(msg instanceof String){
                    System.out.println("接收到："+msg);                
                    ctx.writeAndFlush(Unpooled.copiedBuffer(("服务端反馈："+msg+"\r\n").getBytes("UTF-8")));  
                }else{
                    ByteBuf in = (ByteBuf) msg;  
                    System.out.println("接收到2222："+msg);  
                }          
            }catch(Exception ex){
                ex.printStackTrace();
            }    
            // try {
            //     while (in.isReadable()) { // (1)
            //         System.out.print((char) in.readByte());
            //         System.out.flush();
            //     }
            // } finally {
            //     //Please keep in mind that it is the handler's responsibility to release any reference-counted object passed to the handler.
            //     ReferenceCountUtil.release(msg); // (2)
            // }  
            //no release It is because Netty releases it for you when it is written out to the wire.            
                  
        }
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
            // Close the connection when an exception is raised.
            cause.printStackTrace();
            ctx.close();
        }
    }
    public void aioClientSocket222(){
        AsynchronousSocketChannel aysnCsocket=null;
        try{
            aysnCsocket=AsynchronousSocketChannel.open();
            aysnCsocket.connect(new InetSocketAddress("127.0.0.1",9999),aysnCsocket,new CompletionHandler<Void,AsynchronousSocketChannel>(){
                @Override
                public void completed(Void result, AsynchronousSocketChannel attachment){
                    try{
                        //首次写破冰
                        var welcome=ByteBuffer.allocate(256);
                        welcome.put("hello 我是客户端啊".getBytes("UTF-8"));
                        welcome.flip();
                        attachment.write(welcome).get();//同步写吧
                        //连续读缓存
                        var buffer=ByteBuffer.allocate(256);
                        attachment.read(buffer,10,TimeUnit.SECONDS,buffer,new CompletionHandler<Integer,ByteBuffer>(){
                            @Override
                            public void completed(Integer result, ByteBuffer redadbuffer){
                                try{
                                    if(result==null || result.intValue()==-1){
                                    }else{
                                        //先写后读
                                        if((LocalDateTime.now().getSecond() % 5)==0){
                                            try{//回写
                                                var wbuffer=ByteBuffer.allocate(256);
                                                wbuffer.put(("客户端："+System.currentTimeMillis()).getBytes("UTF-8"));
                                                wbuffer.flip();
                                                attachment.write(wbuffer).get();
                                            }catch(Exception ex){
                                                System.out.println("写异常");
                                                ex.printStackTrace();
                                            }
                                        }                                            
                                        buffer.flip();
                                        var data=new byte[buffer.limit()];
                                        buffer.get(data);
                                        var str=new String(data,"utf-8");
                                        System.out.println("读到服务器数据："+str);
                                        if(!str.equals("bye")){
                                            redadbuffer.clear();//继续异步读
                                            attachment.read(buffer,10,TimeUnit.SECONDS,buffer,this);
                                        }    
                                    }
                                }catch(Exception ex){
                                    ex.printStackTrace();
                                }                                
                            }
                            @Override
                            public void failed(Throwable exc, ByteBuffer redadbuffer){
                                exc.printStackTrace();
                            }
                        });
                    }catch(Exception ex){
                        System.out.println("写异常");
                        ex.printStackTrace();
                    }
                }
                @Override
                public void failed(Throwable exc, AsynchronousSocketChannel attachment){
                    exc.printStackTrace();
                }
            });
            while(true){//等待子工作线程工作
                Thread.sleep(1000);
            }
        }catch(Exception ex){
            ex.printStackTrace();
            if(aysnCsocket!=null){
                try{
                    aysnCsocket.close();
                }catch(Exception ex2){

                }                
            }
        }
    }        
    public void aioServerSocket222(){
        try(var aysnSsocket=AsynchronousServerSocketChannel.open()){
            aysnSsocket.bind(new InetSocketAddress("127.0.0.1",9999));
            aysnSsocket.accept(aysnSsocket,new CompletionHandler<AsynchronousSocketChannel,AsynchronousServerSocketChannel>(){
                @Override
                public void completed(AsynchronousSocketChannel result,AsynchronousServerSocketChannel attachment){
                    attachment.accept(attachment,this);
                    var buffer=ByteBuffer.allocate(256);                    
                    result.read(buffer,10,TimeUnit.SECONDS,buffer,new CompletionHandler<Integer,ByteBuffer>(){
                        @Override
                        public void completed(Integer result222,ByteBuffer attachment){
                            if(result222!=-1){
                                try{
                                    buffer.flip();
                                    var data=new byte[buffer.limit()];
                                    buffer.get(data);
                                    System.out.println("读到数据："+new String(data,"utf-8"));
                                    var wb=ByteBuffer.allocate(256);
                                    wb.put("服务器返回：hello啊".getBytes("UTF-8"));
                                    wb.flip();
                                    result.write(wb).get();//同步阻塞写吧，避免pending问题
                                    //继续读
                                    buffer.clear();
                                    result.read(buffer,10,TimeUnit.SECONDS,buffer,this);
                                }catch(Exception ex){
                                    ex.printStackTrace();
                                }
                            }                               
                        }
                        @Override
                        public void failed(Throwable ex,ByteBuffer attachment){
                            ex.printStackTrace();
                        }
                    });                    
                }
                @Override
                public void failed(Throwable ex,AsynchronousServerSocketChannel attachment){
                    ex.printStackTrace();
                }
            });
            while(true){
                Thread.sleep(1000);
            }            
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }   
    public void aioClientSocket(){
        try(var aysnCsocket=AsynchronousSocketChannel.open()){
            var cfuture=aysnCsocket.connect(new InetSocketAddress("127.0.0.1",9999));
            while(!cfuture.isDone()){
                Thread.sleep(1000);
                System.out.println("waiting...");
            }
            cfuture.get();//暴露异常
            //首次写破冰
            var welcome=ByteBuffer.allocate(256);
            welcome.put("hello 我是客户端啊".getBytes("UTF-8"));
            welcome.flip();
            aysnCsocket.write(welcome).get();
            //连续读缓存
            var buffer=ByteBuffer.allocate(256);
            while(true){   
                //先写后读
                if((LocalDateTime.now().getSecond() % 2)==0){
                    try{//回写
                        var wbuffer=ByteBuffer.allocate(256);
                        wbuffer.put(("客户端："+System.currentTimeMillis()).getBytes("UTF-8"));
                        wbuffer.flip();
                        aysnCsocket.write(wbuffer).get(5000,TimeUnit.MILLISECONDS);
                    }catch(Exception ex){
                        System.out.println("写异常");
                        ex.printStackTrace();
                    }
                }     
                buffer.clear();            
                var readed= aysnCsocket.read(buffer).get(10,TimeUnit.SECONDS);
                if (readed == -1) {//结束了
                    break;
                }    
                buffer.flip();
                var data=new byte[buffer.limit()];
                buffer.get(data);
                var str=new String(data,"utf-8");
                System.out.println("读到服务器数据："+str);
                if(str.equals("bye")){
                    break;
                }                
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }    
    public void aioServerSocket(){
        try(var aysnSsocket=AsynchronousServerSocketChannel.open()){
            aysnSsocket.bind(new InetSocketAddress("127.0.0.1",9999));
            while(true){
                var future=aysnSsocket.accept();
                while(!future.isDone()){
                    Thread.sleep(1000);
                    System.out.println("waiting...");
                }
                try{
                    var csk=future.get();//包裹异常了，需要检测一下啊
                    new Thread(()->{
                        var buffer=ByteBuffer.allocate(256);
                        while(true){
                            try{
                                buffer.clear();
                                //get 是为了确保 read 完成，超时时间可以有效避免DOS攻击，如果客户端一直不发送数据，则进行超时处理
                                var integer=csk.read(buffer).get(5000,TimeUnit.MILLISECONDS);
                                if (integer == -1) {//结束了
                                    break;
                                }                    
                                buffer.flip();
                                var data=new byte[buffer.limit()];
                                buffer.get(data);
                                System.out.println("读到数据："+new String(data,"utf-8"));
                                try{//回写
                                    var wbuffer=ByteBuffer.allocate(256);
                                    wbuffer.put(("服务器："+System.currentTimeMillis()).getBytes("UTF-8"));
                                    wbuffer.flip();
                                    csk.write(wbuffer).get(5000,TimeUnit.MILLISECONDS);
                                }catch(Exception ex){
                                    System.out.println("写异常");
                                    ex.printStackTrace();
                                }     
                            }catch(Exception ex){
                                System.out.println("读异常");
                                ex.printStackTrace();
                                break;
                            }                            
                        }
                    }).start();                    
                }catch(Exception ex){
                    ex.printStackTrace();
                    break;
                }    
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
    /* Future / Callback CompleteHandle
       前者是将异步处理封装在一个Future对象中，会开启一个独立工作线程并被Future对象封装在其内部，用作操控中介和把手
       后者是直接将异步处理完成后的业务逻辑方法作为参数传递过去，在独立工作线程内开始异步操作并在异步操作完成后直接在该独立线程内处理callback方法

    */
    public void aioFileHandle(){        
        try(var aiof=AsynchronousFileChannel.open(Paths.get("C:\\Users\\sunsea\\Desktop\\tmp\\niofrom.txt"),StandardOpenOption.READ);){
            var buffer=ByteBuffer.allocate(128);            
            var future=aiof.read(buffer, 0);
            while(!future.isDone()){
                Thread.sleep(1000);
                System.out.println("waiting...");
            }
            try{
                future.get();//包裹异常了，需要检测一下啊
            }catch(Exception ex){
                ex.printStackTrace();
            }           
            buffer.flip();
            System.out.println("");
            while(buffer.hasRemaining()){
                byte[] data = new byte[buffer.limit()];
                buffer.get(data);
                System.out.print(new String(data,"utf-8"));
            }
            buffer.clear();
            System.out.println("");

            aiof.read(buffer, 0,buffer,new CompletionHandler<Integer,ByteBuffer>(){
                @Override
                public void completed(Integer result, ByteBuffer attachment){
                    System.out.println("CompletionHandler当前线程："+Thread.currentThread().getName());
                    try{
                        if(result==null || result.intValue()<=0){
                            System.out.println("空文件！");
                        }else{
                            attachment.flip();
                            var data=new byte[attachment.limit()];
                            attachment.get(data);                            
                            System.out.print(new String(data,"utf-8"));
                        }
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }                    
                }
                @Override
                public void failed(Throwable exc, ByteBuffer attachment){
                    exc.printStackTrace();
                }
            });

            System.out.println("aioFileHandle当前线程："+Thread.currentThread().getName());
        }catch(Exception ex){
            ex.printStackTrace();
        }        
        try(var aiof=AsynchronousFileChannel.open(Paths.get("C:\\Users\\sunsea\\Desktop\\tmp\\niofrom.txt"),StandardOpenOption.WRITE);){
            var buffer=ByteBuffer.allocate(128);   
            buffer.put("123新信息新信息123".getBytes("UTF-8"));
            buffer.flip();//准备给读
            var future=aiof.write(buffer, 0);//直接从头写入
            while(!future.isDone()){
                Thread.sleep(1000);
                System.out.println("waiting...");
            }
            try{
                future.get();//包裹异常了，需要检测一下啊
            }catch(Exception ex){
                ex.printStackTrace();
            }                                
            System.out.println("已写入11111");
        }catch(Exception ex){
            ex.printStackTrace();
        }      
        try(var aiof=AsynchronousFileChannel.open(Paths.get("C:\\Users\\sunsea\\Desktop\\tmp\\niofrom.txt"),StandardOpenOption.WRITE);){
            var buffer=ByteBuffer.allocate(128);   
            buffer.put("456新信息新信息456".getBytes("UTF-8"));
            buffer.flip();//准备给读
            var future=aiof.write(buffer, aiof.size());//追加append模式
            while(!future.isDone()){
                Thread.sleep(1000);
                System.out.println("waiting...");
            }
            try{
                future.get();//包裹异常了，需要检测一下啊
            }catch(Exception ex){
                ex.printStackTrace();
            }                                
            System.out.println("已写入22222");
        }catch(Exception ex){
            ex.printStackTrace();
        }      
        
    }
    public void servSocketChannelHandle(){
        try(var sel=Selector.open();
            var ssockchannel=ServerSocketChannel.open().bind(new InetSocketAddress(9999));
        ){
            ssockchannel.configureBlocking(false);//设置未非阻塞模式
            ssockchannel.register(sel,SelectionKey.OP_ACCEPT,"hello attachment2222");            
            while(true){                
                if(sel.select() == 0) continue;    
                var it=sel.selectedKeys().iterator();
                while(it.hasNext()){//selector.selectedKeys()返回所有的SelectionKey(包括没有发生事件的)
                    var skey=it.next();
                    it.remove();//删除当前键，避免重复消费                    
                    if(skey.isAcceptable()) {
                        // a connection was accepted by a ServerSocketChannel.
                        //可能监听多个serversocket所以此处需要单独提取
                        ServerSocketChannel serverChannel = (ServerSocketChannel) skey.channel();
                        System.out.println("ServerSocketChannel正在循环监听");
                        SocketChannel clientChannel = null;
                        try{//不能放到资源里面,否则就离开被关闭了无法通信了啊！
                            clientChannel = serverChannel.accept();//接收客户端的连接请求，完成TCP三次握手
                            clientChannel.configureBlocking(false);
                            /* setSoTimeout在非阻塞模式下失效，需要自己设置相关变量来判断  */
                            //clientChannel.socket().setSoTimeout(10000);//设置读取超时，避免dos攻击    
                            //已完成connect所以无法再监听connect
                            clientChannel.register(skey.selector(), SelectionKey.OP_READ);                                            
                            System.out.println("accept 一个 clientChannel");
                        }catch(Exception ex){
                            if(clientChannel!=null) clientChannel.close();
                        }
                    } else if (skey.isReadable()) {
                        System.out.println("可读了");                        
                        SocketChannel clientChannel = (SocketChannel) skey.channel();
                        try{
                            ByteBuffer byteBuffer = ByteBuffer.allocate(256);
                            long bytesRead = clientChannel.read(byteBuffer);
                            while (bytesRead>0){
                                byteBuffer.flip();
                                byte[] data = byteBuffer.array();
                                String info = new String(data).trim();
                                System.out.println("从客户端发送过来的消息是："+info);
                                if(info=="bye"){
                                    clientChannel.close();
                                    break;
                                }
                                byteBuffer.clear();
                                bytesRead = clientChannel.read(byteBuffer);
                                //应答
                                ByteBuffer writeBuffer = ByteBuffer.allocate(256);
                                var writestr=info.length()<100?info:info.substring(0, 100);
                                writeBuffer.put(("服务应答：   "+writestr).getBytes("UTF-8"));
                                writeBuffer.flip();
                                clientChannel.write(writeBuffer);
                            }
                            if (bytesRead==-1){//结束为-1
                                clientChannel.close();
                            }
                        }catch(Exception ex){
                            clientChannel.close();
                            ex.printStackTrace();
                        }                        
                    } else if (skey.isWritable()) {
                        // a channel is ready for writing
                        System.out.println("可写了");
                    }                
                }                            
            }                
        }catch(Exception ex){
            System.out.println(ex);
            ex.printStackTrace();
        }        
    }    
    public void socketChannelHandle(){
        try(var sel=Selector.open();
            var sockchannel=SocketChannel.open();
        ){
            sockchannel.configureBlocking(false);//设置未非阻塞模式
            var sckey = sockchannel.register(sel,SelectionKey.OP_CONNECT,"hello attachment");
            //为非阻塞noblock模式监听连接，需要将此语句放置在sockchannel.configureBlocking(false);之后！！！
            sockchannel.connect(new InetSocketAddress(9999));
            var buffForWrite=ByteBuffer.allocate(256);
            while(true){         
                if((LocalDateTime.now().getSecond() % 2)==0){
                    var write=(" 客户端动态附加内容："+LocalDateTime.now().toLocalTime()).getBytes("UTF-8");
                    if(buffForWrite.position()+write.length > buffForWrite.capacity()){
                        buffForWrite.clear();
                    }
                    buffForWrite.put(write);
                    if(sckey.isValid()){
                        sckey.interestOpsOr(SelectionKey.OP_WRITE);      
                    }                    
                }
                if(sel.select() == 0) continue;   
                var it=sel.selectedKeys().iterator();
                while(it.hasNext()){
                    var skey=it.next();
                    it.remove();                    
                    if(!skey.isValid()) continue;
                    if (skey.isConnectable()){                        
                        SocketChannel clientChannel = (SocketChannel) skey.channel();
                        if (clientChannel.isConnectionPending()){
                            //在非阻塞模式下connect也是非阻塞的，所以要确保连接已经建立完成
                            while (!clientChannel.finishConnect()) {
                                System.out.println("连接中");
                            }
                        }      
                        //clientChannel.register(skey.selector(), SelectionKey.OP_READ);  
                        //也可以直接skey上新增监听事件
                        skey.interestOpsOr(SelectionKey.OP_READ);     
                        //
                        var write="服务端你好,我是客户端啊啊啊啊".getBytes("UTF-8");
                        if(buffForWrite.position()+write.length > buffForWrite.capacity()){
                            buffForWrite.clear();
                        }
                        buffForWrite.put(write);
                        skey.interestOpsOr(SelectionKey.OP_WRITE);                                           
                    }else if (skey.isReadable()){
                        System.out.println("可读了");
                        SocketChannel clientChannel = (SocketChannel) skey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(256);
                        //捕获异常，因为在服务端关闭后会发送FIN报文，会触发read事件，但连接已关闭,此时read()会产生异常
                        try {
                            int bytesRead = clientChannel.read(byteBuffer);                            
                            while (bytesRead>0){
                                byteBuffer.flip();
                                byte[] data = byteBuffer.array();
                                String info = new String(data).trim();
                                System.out.println("从服务端发送过来的消息是："+info);
                                if(info=="bye"){
                                    clientChannel.close();
                                    throw new RuntimeException("通信结束了！");
                                }
                                byteBuffer.clear();
                                bytesRead = clientChannel.read(byteBuffer);
                            }
                            if (bytesRead==-1){//结束为-1
                                clientChannel.close();
                                throw new RuntimeException("通信结束了！");
                            }
                        } catch (IOException e) {
                            System.out.println("服务器异常，请联系客服人员!正在关闭客户端.........");
                            skey.cancel();
                            clientChannel.close();
                            throw e;
                        }                        
                    }else if (skey.isWritable()){
                        System.out.println("可写了");  
                        SocketChannel clientChannel = (SocketChannel) skey.channel();                      
                        buffForWrite.flip();
                        if(buffForWrite.hasRemaining()){
                            while(buffForWrite.hasRemaining()){
                                clientChannel.write(buffForWrite);  
                            }                            
                            buffForWrite.clear();
                        }                        
                        //写完就取消写事件
                        skey.interestOps(skey.interestOps() & ~SelectionKey.OP_WRITE);
                    }
                }  

            }                
        }catch(Exception ex){
            System.out.println(ex);
            ex.printStackTrace();
        }        
    }
    /* channel 基于块缓存的双向的处理模式，需要注意close
       FileChannel/ServerSocketChannel/SocketChannel
       默认是block模式，文件通道只支持同步模式！！！socket支持非阻塞模式！
    */
    public void channelHandle(){
        try(var fcin = (new RandomAccessFile("C:\\Users\\sunsea\\Desktop\\tmp\\niofrom.txt", "rw")).getChannel();
            var fcout = (new RandomAccessFile("C:\\Users\\sunsea\\Desktop\\tmp\\nioto.txt", "rw")).getChannel();
        ){
            var buff=ByteBuffer.allocate(100);
            while(fcin.read(buff)!=-1){//此处是阻塞模式
                buff.flip();
                //此处是阻塞模式
                fcout.write(buff);//未开启append模式则头写入，否则从尾部写入
                buff.clear();
            }
            //直接传输模式
            //fcout.transferFrom(fcin,0, fcin.size());
            //fcin.transferTo(0, fcin.size(),fcout);
        }catch(Exception ex){
            System.out.println(ex.toString());
        }
    }
    public void bufferHandle(){
        /* 初始化时 position=0 limit=capacity=length mark=-1
            使用过程中应用逻辑需要自己处理是否进行flip
            写完后的读需要flip，而flip时会强制切换 limit=position position=0 mask=-1
            读完可以设置clear() 同初始化逻辑 position=0 limit=capacity=length mark=-1
                也可以compact  会将positon->limit内容移动到0->limit-position区域，然后设置postion=limit 
        */
        var buf=CharBuffer.allocate(10);
        buf.put("中国人民万岁");//写超出buffer capacity则会爆异常的
        buf.flip();
        System.out.println(buf.get());//递增position
        System.out.println(buf.get(5));//不递增position
        buf.compact();//将剩余的挪移到buffer头区，并设置position=limit，准备新的写入
        buf.put("这是很好的");
        buf.flip();//再次读
        while(buf.hasRemaining()){
            System.out.println(buf.get());
        }
    }
}
